package com.googlecode.kipler.container.dl;

import java.util.HashSet;
import java.util.Iterator;

import com.googlecode.kipler.common.Copyable;
import com.googlecode.kipler.container.generic.MultivalueHashMap;
import com.googlecode.kipler.syntax.formula.BooleanFormula;
import com.googlecode.kipler.syntax.formula.Formula;
import com.googlecode.kipler.syntax.formula.LTLBinaryFormula;
import com.googlecode.kipler.syntax.formula.LTLUnaryFormula;

/**
 * 
 * @author İnanç Seylan
 * 
 */
public class FormulaCategorizer implements Copyable<FormulaCategorizer> {

	private MultivalueHashMap<FormulaCategory, Formula> library = new MultivalueHashMap<FormulaCategory, Formula>();

	private FormulaCategory findCategory(Formula f) {
		FormulaCategory result = null;
		if (f instanceof BooleanFormula) {
			BooleanFormula bd = (BooleanFormula) f;
			result = (bd.getOperator() == BooleanFormula.Operator.AND) ? FormulaCategory.AND
					: FormulaCategory.OR;
		} else if (f instanceof LTLUnaryFormula) {
			LTLUnaryFormula unFor = (LTLUnaryFormula) f;
			switch (unFor.getConstructor()) {
			case ALWAYS:
				result = FormulaCategory.ALWAYS;
				break;
			case SOMETIME:
				result = FormulaCategory.SOMETIME;
				break;
			case NEXT:
				result = FormulaCategory.NEXT;
				break;
			}
		} else if (f instanceof LTLBinaryFormula) {
			LTLBinaryFormula binFor = (LTLBinaryFormula) f;
			switch (binFor.getConstructor()) {
			case BEFORE:
				result = FormulaCategory.BEFORE;
				break;
			case UNTIL:
				result = FormulaCategory.UNTIL;
				break;
			}
		}
		return result;
	}

	public void add(Formula f) {
		FormulaCategory cat = findCategory(f);
		library.put(cat, f);
	}

	public boolean contains(Formula f) {
		FormulaCategory cat = findCategory(f);
		return library.containsKeyValue(cat, f);
	}

	public Iterator<Formula> formulaIterator() {
		return library.values().iterator();
	}

	public Iterator<Formula> categoryIterator(FormulaCategory category) {
		/*
		 * Return a copy of the set because while iterating over formulas in the
		 * rules, we might also add formulas to the same category. This prevents
		 * concurrent modification exception.
		 */
		return new HashSet<Formula>(library.getValuesForKey(category))
				.iterator();
	}

	@SuppressWarnings("unchecked")
	public FormulaCategorizer copy() {
		FormulaCategorizer categorizer = new FormulaCategorizer();
		categorizer.library = (MultivalueHashMap<FormulaCategory, Formula>) this.library
				.copy();
		return categorizer;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj != null && (obj.getClass().equals(this.getClass()))) {
			FormulaCategorizer other = (FormulaCategorizer) obj;
			return library.equals(other.library);
		}
		return false;
	}

	@Override
	public int hashCode() {
		return library.hashCode();
	}

	protected boolean containsAllFormulasOf(FormulaCategorizer other) {
		boolean result = library.keySet().containsAll(other.library.keySet());
		Iterator<FormulaCategory> i = other.library.keySet().iterator();
		while (result && i.hasNext()) {
			FormulaCategory category = i.next();
			result = result
					&& library.getValuesForKey(category).containsAll(
							other.library.getValuesForKey(category));
		}
		return result;
	}

	protected boolean hasSameFormulasAs(FormulaCategorizer other) {
		boolean result = library.keySet().equals(other.library.keySet());
		Iterator<FormulaCategory> i = other.library.keySet().iterator();
		while (result && i.hasNext()) {
			FormulaCategory category = i.next();
			result = result
					&& library.getValuesForKey(category).equals(
							other.library.getValuesForKey(category));
		}
		return result;
	}

	public int size() {
		return library.valueSize();
	}

}
